﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.IO.Compression;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Zip.Compression;

namespace MrpReader
{
    public static class Program
    {
        //mrp文件结构
        public class MrpInfo
        {
            public string mineType;//0-3 "MRPG"
            public uint fileListEndOffset;//4-7 文件列表结束位置+8
            public uint fileListStartOffset;//12-15 文件列表起始位置
            public string internalName;//16-27 内部名（总最长12字节，可用11字节）
            public string displayName;//28-51 显示名（总最长24字节，可用23字节）
            public string serialNumber;//52-61 串号（总最长10字节，可用9字节）
            public byte[] tempData1;//62-67 6字节0
            public uint appId;//68-71 软件编号APPID（注意一般是 小端位序）
            public uint versionId;//72-75 版本ID（注意一般是 小端位序）
            public uint visibleInList;//76-79 列表可见 及 CPU数信息 及是否Shell启动信息（偶数表示列表不可见，奇数表示可见，可见+3个CPU+shell=x0F ，不可见+3个CPU=x06）
            public uint tempData2;//80-83 未知，现在一般为x 12 27 00 00
            public uint tempData3;//84-87 未知
            public string productInfo;//88-127 出品商信息（总最长40字节，可用39字节）
            public string Description;//128-191 软件描述（总最长64字节，可用63字节）
            public uint APPID;//192-195 软件编号APPID（注意一般是 大端位序，此处为列表识别APPID）
            public uint SID;//196-199 版本ID（注意一般是 大端位序，此处为列表识别ID）
            public byte[] extraData;//200-239 备用
            public List<mrpItem> mrpItems = new List<mrpItem>();



        }
        //mrp文件包含的文件
        public class mrpItem
        {
            public string fileName;
            public byte[] fileContent;
            public uint fileStartOffset;
            public uint fileLength;
        }
        public static string mrpPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Res/applist.mrp");
        public static FileStream fileStream;
        public static MrpInfo mrpInfo_ = new MrpInfo();
        //设置单线程单元
        [STAThread]
        public static void Main()
        {
            fileStream = File.OpenRead(mrpPath);
            mrpInfo_ = ReadMrpInfo(fileStream);
            fileStream.Close();
            Console.WriteLine("欢迎使用MRP读取器v1.0");
            while (true)
            {
                Console.Write("MrpReader->");
                string s = Console.ReadLine();
                if (s.Contains(" "))
                {
                    string[] inputs = s.Split(new char[] { ' ' });
                    if (inputs.Length < 2)
                    {
                        Console.WriteLine("Error input commond");
                    }
                    switch (inputs[0])
                    {
                        case "ex":
                            {
                                int.TryParse(inputs[1], out int pos);
                                if (pos >= mrpInfo_.mrpItems.Count)
                                {
                                    Console.WriteLine("导出位置不正确");
                                }
                                else
                                {
                                    mrpItem mrpItem = mrpInfo_.mrpItems[pos];
                                    File.WriteAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, mrpItem.fileName), mrpItem.fileContent);
                                    Console.WriteLine($"导出文件{mrpItem.fileName}成功");
                                }
                                break;
                            }
                        default: Console.WriteLine("未能识别的命令"); break;
                    }
                }
                else {
                    switch (s) {
                        case "se":
                            {
                                OpenFileDialog openFileDialog = new OpenFileDialog();
                                openFileDialog.Filter = "*.mrp|*.mrp";
                                openFileDialog.FileOk += (a, b) => {
                                    if (b.Cancel)
                                    {
                                        Console.WriteLine("取消选择");
                                    }
                                    else {
                                        OpenFileDialog fileDialog = (OpenFileDialog)a;
                                        FileStream fileStream = File.OpenRead(fileDialog.FileName);
                                        mrpInfo_ = ReadMrpInfo(fileStream);
                                        fileStream.Close();
                                        Console.WriteLine("加载"+fileDialog.FileName);
                                        List();
                                    }

                                };
                                openFileDialog.ShowDialog();

                                break;
                            }
                        case "ls": { List();break; }
                        default:Console.WriteLine("未能识别的命令");break;
                    }
                
                }
            }
        }


        public static void List() {
            Console.WriteLine($"名称:{mrpInfo_.displayName}\n介绍:{mrpInfo_.Description}\n开始位置:{mrpInfo_.fileListStartOffset:x} 结束位置: {mrpInfo_.fileListEndOffset:x}");
            int i = 0;
            Console.WriteLine("====文件列表====");
            foreach (mrpItem mrpItem in mrpInfo_.mrpItems)
            {
                Console.WriteLine($"{i++}.{mrpItem.fileName}           len:{mrpItem.fileLength}");
            }
        }
        public static MrpInfo ReadMrpInfo(FileStream fileStream)
        {
            MrpInfo mrpInfo = new MrpInfo();
            fileStream.Seek(0, SeekOrigin.Begin);
            mrpInfo.mineType = GetString(ReadBytes(fileStream, 0, 4));
            mrpInfo.fileListEndOffset = GetIntT(ReadBytes(fileStream, 4, 4));
            mrpInfo.fileListEndOffset += 8;
            mrpInfo.fileListStartOffset = GetIntT(ReadBytes(fileStream, 12, 4));
            mrpInfo.internalName = GetString(ReadBytes(fileStream, 16, 12));
            mrpInfo.displayName = GetString(ReadBytes(fileStream, 28, 24));
            mrpInfo.serialNumber = Encoding.ASCII.GetString(ReadBytes(fileStream, 52, 10));
            mrpInfo.tempData1 = ReadBytes(fileStream, 62, 6);
            mrpInfo.appId = GetIntT(ReadBytes(fileStream, 68, 4));
            mrpInfo.versionId = GetIntT(ReadBytes(fileStream, 72, 4));
            mrpInfo.visibleInList = GetIntT(ReadBytes(fileStream, 76, 4));
            mrpInfo.tempData2 = GetIntT(ReadBytes(fileStream, 80, 4));
            mrpInfo.tempData3 = GetIntT(ReadBytes(fileStream, 84, 4));
            mrpInfo.productInfo = GetString(ReadBytes(fileStream, 88, 40));
            mrpInfo.Description = GetString(ReadBytes(fileStream, 128, 64));
            mrpInfo.APPID = GetIntT(ReadBytes(fileStream, 192, 4));
            mrpInfo.SID = GetIntT(ReadBytes(fileStream, 196, 4));
            mrpInfo.extraData = ReadBytes(fileStream, 200, 40);
            uint offset = mrpInfo.fileListStartOffset;

            //一个文件的组成格式为 文件名长度+文件名+文件内容的位置偏移+文件长度+四字节0x00

            while (offset < mrpInfo.fileListEndOffset)
            {
                if (offset > fileStream.Length) break;
                mrpItem mrpItem = new mrpItem();
                //开始读取mrpItem
                //文件名长度
                uint flen = GetIntT(ReadBytes(fileStream, (int)offset, 4));
                offset += 4;
                mrpItem.fileName = GetString(ReadBytes(fileStream, (int)offset, (int)flen));
                offset += flen;
                uint fileOffset = GetIntT(ReadBytes(fileStream, (int)offset, 4));
                offset += 4;
                uint fileLen = GetIntT(ReadBytes(fileStream, (int)offset, 4));
                offset += 8;//长度4个字节+空读4个字节
                mrpItem.fileContent = Unzip(ReadBytes(fileStream, (int)fileOffset, (int)fileLen));
                mrpItem.fileLength = fileLen;
                mrpItem.fileStartOffset = fileOffset;
                mrpInfo.mrpItems.Add(mrpItem);
            }




            return mrpInfo;
        }



        public static uint GetInt(byte[] tmp)
        {
            return ((uint)tmp[0]) << 24 | ((uint)tmp[1]) << 16 | ((uint)tmp[2]) << 8 | tmp[3];
        }
        public static uint GetIntT(byte[] tmp)
        {
            return ((uint)tmp[3]) << 24 | ((uint)tmp[2]) << 16 | ((uint)tmp[1]) << 8 | tmp[0];
        }

        public static string GetString(byte[] tmp)
        {
            List<byte> mm = new List<byte>();
            foreach (byte m in tmp)
            {
                if (m == 0) break;
                mm.Add(m);
            }


            return Encoding.UTF8.GetString(Encoding.Convert(Encoding.GetEncoding("GBK"), Encoding.UTF8, mm.ToArray()));
        }
        public static byte[] ReadBytes(FileStream fileStream, int start, int len)
        {
            fileStream.Seek(start, SeekOrigin.Begin);
            List<byte> vs = new List<byte>();
            for (int i = 0; i < len; i++)
            {
                vs.Add((byte)fileStream.ReadByte());
            }
            return vs.ToArray();
        }

        public static byte[] Unzip(byte[] vs) {
            try
            {
                byte[] d = new byte[1024];
                List<byte> bys = new List<byte>();
                MemoryStream memoryStream = new MemoryStream(vs);
                GZipStream gZipStream = new GZipStream(memoryStream,CompressionMode.Decompress,true);
                int len = 0;
                while ((len = gZipStream.Read(d, 0, 1024)) > 0)
                {
                    for (int i=0;i<len;i++) {
                        bys.Add(d[i]);
                    }
                }
                return bys.ToArray();
            }
            catch {
                Console.WriteLine("解压失败");
                return vs;
            }
        }


    }
}
